{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ab9e2e97-7f10-4353-859e-693842bde465",
   "metadata": {},
   "source": [
    "In this notebook, we will prepare the model and dataset for the subsequent steps.\n",
    "\n",
    "Let's define the paths to the model and the dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73efb325",
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: Change to the model you want to use\n",
    "HF_MODEL_NAME_OR_PATH = \"qwen/Qwen3-8B\"\n",
    "\n",
    "ROOT_DIR = \"/workspace\"\n",
    "NEMO_OUTPUT_PATH = f\"{ROOT_DIR}/Qwen3-8B-nemo\"\n",
    "DATA_PATH = f\"{ROOT_DIR}/wikitext-data\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fca2355f",
   "metadata": {},
   "source": [
    "### Step 1: Convert the Hugging Face model to NeMo checkoint format\n",
    "\n",
    "You can skip this step if you already have the model in NeMo 2.0 checkpoint format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84cb6425",
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: Change to the model you want to use. For Llama, you need to use llm.LlamaModel(llm.Llama31Config8B() instead of llm.Qwen3Model(llm.Qwen3Config8B())\n",
    "!python -c 'from nemo.collections import llm; llm.import_ckpt(llm.Qwen3Model(llm.Qwen3Config8B()), source=\"hf://{HF_MODEL_NAME_OR_PATH}\", output_path=\"{NEMO_OUTPUT_PATH}\")'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78831e12",
   "metadata": {},
   "source": [
    "This is an example of what the nemo checkpoint should look like:\n",
    "\n",
    "```\n",
    "Qwen3-8B-nemo/\n",
    "├── context/\n",
    "│   ├── artifacts/\n",
    "│   │   └── generation_config.json\n",
    "│   ├── nemo_tokenizer/\n",
    "│   │   ├── added_tokens.json\n",
    "│   │   ├── chat_template.jinja\n",
    "│   │   ├── merges.txt\n",
    "│   │   ├── special_tokens_map.json\n",
    "│   │   ├── tokenizer.json\n",
    "│   │   ├── tokenizer_config.json\n",
    "│   │   └── vocab.json\n",
    "│   ├── io.json\n",
    "│   └── model.yaml\n",
    "└── weights/\n",
    "    ├── .metadata\n",
    "    ├── __0_0.distcp\n",
    "    ├── __0_1.distcp\n",
    "    ├── common.pt\n",
    "    └── metadata.json\n",
    "```\n",
    "\n",
    "\n",
    "`NOTE:` If you wish to convert the NeMo models back to Hugging Face format after pruning and distillation, you can use the following command:\n",
    "\n",
    "```bash\n",
    "python -c 'from nemo.collections import llm; llm.export_ckpt(path=\"<NEMO_MODEL_PATH>\", target=\"hf\", output_path=\"<HF_OUTPUT_PATH>\")'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16a9e822",
   "metadata": {},
   "source": [
    "### Step 2: Prepare the dataset\n",
    "\n",
    "**Obtain the dataset**: Generate the `wikitext-train.jsonl` split from the [WikiText-103-v1](https://huggingface.co/datasets/Salesforce/wikitext/viewer/wikitext-103-v1) dataset.\n",
    "\n",
    "> `NOTE:` While this notebook uses the `wikitext` dataset as it is the most easy to get started with, in practice, we recommend using bigger, more recent and much higher quality datasets like [ClimbMix](https://huggingface.co/datasets/OptimalScale/ClimbMix) or [Nemotron-Pretraining-SFT-v1](https://huggingface.co/datasets/nvidia/Nemotron-Pretraining-SFT-v1). These datasets are often split into multiple partitions so you would need to tokenize each partition separately."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a3a359a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "import os\n",
    "\n",
    "from datasets import load_dataset\n",
    "\n",
    "# Load the WikiText-103 dataset\n",
    "dataset = load_dataset(\"wikitext\", \"wikitext-103-v1\", split=\"train\")\n",
    "\n",
    "# Define the destination folder\n",
    "os.makedirs(DATA_PATH, exist_ok=True)\n",
    "\n",
    "# Save splits to JSONL files and calculate their sizes\n",
    "with open(f\"{DATA_PATH}/wikitext-train.jsonl\", \"w\") as file:\n",
    "    for item in dataset:\n",
    "        file.write(json.dumps(item) + \"\\n\")\n",
    "\n",
    "print(f\"Raw dataset saved to {DATA_PATH}/wikitext-train.jsonl\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7a6333c",
   "metadata": {},
   "source": [
    "**Tokenize the dataset**: Tokenize the dataset using the model's tokenizer to convert the data into a memory map format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6505c00b-9eb4-4087-9e49-423f6228e690",
   "metadata": {
    "scrolled": true,
    "tags": []
   },
   "outputs": [],
   "source": [
    "from modelopt.torch.utils.plugins import megatron_preprocess_data\n",
    "\n",
    "megatron_preprocess_data(\n",
    "    input_path=f\"{DATA_PATH}/wikitext-train.jsonl\",\n",
    "    output_dir=DATA_PATH,\n",
    "    tokenizer_name_or_path=HF_MODEL_NAME_OR_PATH,\n",
    "    json_keys=[\"text\"],\n",
    "    workers=32,\n",
    "    log_interval=100000\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d77ee8a-e0dc-44f7-b5e8-3b6025d979d7",
   "metadata": {},
   "source": [
    "After running the above scripts, you will see the tokenized `<ROOT_DIR>/wikitext-data/wikitext-train_text_document.{bin/idx}` files. As we can see from the log, the tokenized dataset has only 125M tokens. In practice, for distillation of a pruned model, we recommend using atleast 50B tokens. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
